package es.xpt.ysf.utils;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import org.jboss.vfs.VirtualFile;
import org.jboss.vfs.VirtualFileFilter;

import es.xpt.ysf.logging.LogManager;


/**
 * <p>Utility class with methods that support getting a recursive list of
 * classes starting with a specific package name.</p>
 */
public final class PackageInfo {


    /**
     * <p>Return an array of all classes, visible to our application class loader,
     * in the specified Java package.</p>
     *
     * @param classes List of matching classes being accumulated
     * @param pckgname Package name used to select matching classes
     *
     * @throws ClassNotFoundException
     * @throws IOException 
     */
    public List<Class<?>> getClasses(final String pckgname) throws ClassNotFoundException  {

        Enumeration<?> resources;
        ClassLoader cld;
        String path;
        try {
            // convert the package name to a path
            path = pckgname.replace('.', '/');
            cld = Thread.currentThread().getContextClassLoader();
            if (cld == null) {
                throw new ClassNotFoundException("Can't get class loader.");
            }

            // find the entry points to the classpath
            resources = cld.getResources(path);
            if (resources == null || !resources.hasMoreElements()) {
                throw new ClassNotFoundException("No resource for " + path);
            }

        } catch (NullPointerException e) {
            throw(ClassNotFoundException) new ClassNotFoundException(pckgname + " (" + pckgname
                    + ") does not appear to be a valid package", e);
        } catch (IOException e) {
            throw(ClassNotFoundException) new ClassNotFoundException(pckgname + " (" + pckgname
                    + ") does not appear to be a valid package", e);
        }
        
        List<Class<?>> classes = new ArrayList<Class<?>>();

        // iterate through all resources containing the package in question
        while (resources.hasMoreElements()) {
            URL resource = (URL) resources.nextElement();
            try {
            	URLConnection connection = resource.openConnection();
	        	VirtualFile vf = (VirtualFile)connection.getContent();
	        	List<VirtualFile> list = vf.getChildrenRecursively(new VirtualFileFilter() {
	
					@Override
					public boolean accepts(VirtualFile file) {
						if ( file.isDirectory() ) {
							return false;
						}
						if (!file.getName().toLowerCase().endsWith(".class")) {
	                        return false;
	                    }
						return true;
					}
	
	            });
	        	
	        	for ( VirtualFile f : list ) {
	        		String className = pckgname + "." + f.asFileURL().toString().substring(resource.toString().length(), f.asFileURL().toString().length()-6).replace('/', '.');
	                Class<?> c = loadClass(cld, className);
	                if ( c!=null && c.getInterfaces().length > 0) {
	                	classes.add(c);
	                }
	        		
	        	}
            } catch (IOException e) {
                throw(ClassNotFoundException) new ClassNotFoundException(pckgname + " (" + pckgname
                        + ") does not appear to be a valid package", e);
            } finally {
            	
            }
        	

        }

        if (classes.size() < 1) {
            throw new ClassNotFoundException(pckgname + " does not appear to be a valid package");
        }

        return classes;
    }


    /**
     * <p>Load the class <code>className</code> using the classloader
     * <code>cld</code>, and add it to the list.</p>
     *
     * @param classes List of matching classes being accumulated
     * @param cld ClassLoader from which to load the specified class
     * @param className Name of the class to be loaded
     */
    private Class<?> loadClass(ClassLoader cld, String className) {

        try {
            return cld.loadClass(className);
        }
        catch (NoClassDefFoundError e) {
            LogManager.getLog(getClass()).warn("error loading class " + className, e);
        } catch (ClassNotFoundException e) {
        	LogManager.getLog(getClass()).warn("error loading class " + className, e);
        }
        return null;
    }



 
}

